#pragma once
#include <iostream>
#include <cstring>
#include "Sock.hpp"
#include <jsoncpp/json/json.h>

// protocol协议：约定协议-写入格式和输出格式。(通过序列化，反序列化来完成)

namespace ns_protocol
{

// #define MYSELF 1
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define SEP "\r\n"
#define SEP_LEN strlen(SEP)//不能为sizeof(),因为""默认会有一个\0，我们想要的长度是2，但是包括\0，长度就为3了
    class Request // 接收数据
    {
    public:
        // 传输控制协议
        // 方案1.自主实现 "lenth\r\nx_ op_ y_\r\n"，这里的\r\n是特殊标识符，不计入长度，用来区分长度和正文的,\r\n可以替换成其他的，但是\r\n的可读性非常好
        //范例:lenth\r\nx_ op_ y_\r\nlenth\r\nx_ op_ y_\r\nlenth\r\nx_ op_ y_\r\n，一串数据。
        //例子:
        //9
        //123 + 456
        //5
        //2 + 3
        //6
        //12 + 3

        // 方案2.使用现成方法

        
        std::string Serialize() // 序列化-读取，写入，传输协议
        {
#ifdef MYSELF
            // 方案1.自主实现 "x_ op_ y_"
            std::string str;
            str = std::to_string(x_);
            str += SPACE;
            str += op_;
            str += SPACE;
            str += std::to_string(y_);

            return str;

            // //my
            // std::string str;
            // str=std::to_string(x_);
            // str+=SPACE;
            // str+=op_;
            // str+=SPACE;
            // str+=std::to_string(y_);

#else
            // 方案2.使用现成的方案(json，自动的序列化)
           Json::Value root;
           root["x_"]=x_;
           root["y_"]=y_;
           root["op_"]=op_;
           Json::FastWriter writer;
           return writer.write(root);//完成了序列化

#endif
        }

        //"x_ op_ y_"的反序列化
        bool Deserialize(const std::string &str /*字符串*/) // 反序列化
        {
#ifdef MYSELF
            // 方案1.自主实现方案
            std::size_t left = str.find(SPACE);
            if (left == std::string::npos)
                return false;
            std::size_t right = str.rfind(SPACE);
            if (right == std::string::npos)
                return false;
            x_ = atoi(str.substr(0, left).c_str());           // 前闭后开.[0,SPACE位置)
            y_ = atoi(str.substr(right + SPACE_LEN).c_str()); // 前闭后开
            if (left + SPACE_LEN >= str.size())
                return false;
            else
                op_ = str[left + SPACE_LEN];

                // std::size_t left=str.find(SPACE);
                // if(left==std::string::npos)return false;
                // std::size_t right=str.rfind(SPACE);
                // if(right==std::string::npos)return false;

                // x_=atoi(str.substr(0,left).c_str());
                // y_=atoi(str.substr(right+SPACE_LEN).c_str());
                // if(left+SPACE_LEN>=str.size())return false;
                // else
                // op_=str[left+SPACE_LEN];

#else
            // 方案2.使用现成的方案(json，自动反序列化)
            Json::Value root;
            Json::Reader reader;
            reader.parse(str/*传进去需要解析的字符串*/,root/*反序列化之后提取的结果放在哪-输出型参数*/);
            x_=root["x_"].asInt();
            y_=root["y_"].asInt();
            op_=root["op_"].asInt();
            
            return true;

#endif
        }

    public:
        Request() {}
        Request(int x, int y, char op) : x_(x), y_(y), op_(op) // 带参构造
        {
        }

        // Request(Request& rq)//拷贝构造
        // {

        // }

        ~Request() {}

    public:
        // 约定，就叫协议
        int x_;
        int y_;
        char op_; //'+' '-' '*' '/' '%'
    };






    class Response
    {
    public:
        // "code_ result_"
        std::string Serialize()
        {
#ifdef MYSELF
            std::string s;
            s = std::to_string(code_);
            s += SPACE;
            s += std::to_string(result_);

            return s;

            ////my
            // std::string s;
            // s=std::to_string(code_);
            // s+=SPACE;
            // s+=std::to_string(result_);
            // return s;
#else
            // 方案2.使用现成的方案(json，自动序列化)
           Json::Value root;
           root["result_"]=result_;
           root["code_"]=code_;
           root["xx"]=xx;
           root["yy"]=yy;
           root["opp"]=opp;

           Json::FastWriter writer;
           return writer.write(root);//完成了序列化

#endif
        }
        // "111 100"
        bool Deserialized(const std::string &s)
        {
#ifdef MYSELF
            std::size_t pos = s.find(SPACE);
            if (pos == std::string::npos)
                return false;
            code_ = atoi(s.substr(0, pos).c_str());
            result_ = atoi(s.substr(pos + SPACE_LEN).c_str());
            return true;

            // //my
            // std::size_t pos=s.find(SPACE);
            // if(pos==std::string::npos)return false;
            // code_=atoi(s.substr(0,pos).c_str());
            // result_=atoi(s.substr(pos+SPACE_LEN).c_str());
            // return true;

#else
             // 方案2.使用现成的方案(json，自动反序列化)
            Json::Value root;
            Json::Reader reader;
            reader.parse(s/*传进去需要解析的字符串*/,root/*反序列化之后提取的结果放在哪-输出型参数*/);
            result_=root["result_"].asInt();
            code_=root["code_"].asInt();
            xx=root["xx"].asInt();
            yy=root["yy"].asInt();
            opp=root["opp"].asInt();
            
            return true;

#endif
        }

    public:
        Response() {}
        Response(int result, int code,int x,int y,int op) : result_(result), code_(code),xx(x),yy(y),opp(op)
        {
        }
        ~Response() {}

    public:
        int result_; // 计算结果
        int code_;   // 计算结果的状态码-可能会有除零错误等等错误，需要

        int xx;
        int yy;
        char opp;
    };


    // //方案1.recv
    // std::string Recv(int sock) // 给客户端收消息用
    // {
    //     // TCP是面向字节流的,UDP是面向数据报的。这里是TCP面向字节流。
    //     char inbuffer[1024];
    //     ssize_t s = recv(sock, inbuffer, sizeof(inbuffer), 0); // 当客户端关闭sock后，s==0读到0个字符.然后返回""空串给客户端序列化反序列化，然后向serversoc进行send，此时会报错。再接收会报异常，使整个进程退出
    //     if (s > 0)
    //         return inbuffer;
    //     else if (s == 0)
    //         std::cout << "client quit" << std::endl; // 只有客户端会退出
    //     else
    //         std::cout << "recv error" << std::endl;
    //     return "";
    // }

     //方案2.recv-添加了传输控制协议，确保返回一个完整的报文。
    bool Recv(int sock,std::string* out/*输出型参数，用于返回接收的信息,代替的返回值返回，用参数进行返回*/) // 给客户、服务端收消息用
    {
        
        // TCP是面向字节流的,UDP是面向数据报的。这里是TCP面向字节流。
        char buffer[1024];
        ssize_t s = recv(sock, buffer, sizeof(buffer)-1/*这里如果减一，则是当成字符串看，如果不是减一则是当成整数看。-1的目的是为了在buffer[s]处设置个\0表示结尾*/, 0); //传输控制协议的读取上来的例子:9\r\n123+ 、 5\r\n12+331\n\r
        if (s > 0)
        {
            buffer[s]=0;
            *out+=buffer;
        }
        else if (s == 0)
        {
            // std::cout<<"client quit"<<std::endl;
            return false;
        }
        else
        {
             std::cout << "recv error" << std::endl;
             return false;
        }
        return true;
    }

    void Send(int sock, const std::string str) // 给客户、服务端发消息用
    {
        // std::cout << "send in" << std::endl;
        int n = send(sock, str.c_str(), str.size(), 0);
        if (n < 0)
            std::cout << "send error" << std::endl; // 对方链接已经关了，再发送，则报错。不过这里的调试信息是看不到的，因为在send那就已经报错了.
    }

    //自主实现："lenth\r\nx_ op_ y_\r\n"--->x_ op_ y_
    std::string Decode(std::string&buffer/*输入输出型参数*/)//解析buffer，解析从recv接收缓冲区中获取的数据
    {
        std::size_t pos=buffer.find(SEP);
        if(pos==std::string::npos)return "";
        int content_size=buffer.size()-pos-SEP_LEN*2;//计算当前正文长度,用于后续和lenth比较,保证正文内容完整:计算正文的长度，判断是否>=len的长度，如果>=，则表明去除掉分隔符和lenth长度之后，剩下的数据足以提取一个完整的报文
        int size=atoi(buffer.substr(0,pos).c_str());//获取lenth，报文正文长度
        if(content_size>=size)
        {
            //至少具有一个合法的报文

            //提取成功，进行解析.一步步提取正文
            buffer.erase(0,pos+SEP_LEN);//将lenth\r\n先删掉
            std::string s=buffer.substr(0,size);//前面已经剪掉了lenth了，现在的0处是从正文开始
            buffer.erase(0,size+SEP_LEN);//提取完成了，将 正文\r\n删掉，为提取下一个报文正文做准备
            return s;
        }
        else 
        return "";//此时报文不完整，提取不成功，返回空串
    }

    //"XXXXX"--> "5\r\nXXXXX\r\n"
    std::string Encode(std::string &s/*输入输出型参数*/)//添加长度信息-添加协议
    {
        std::string new_package=std::to_string(s.size());
        new_package+=SEP;
        new_package+=s;
        new_package+=SEP;
        return new_package;
    }
}